package com.springboot.nacos.config.refresh;


import small.common.crypto.util.KeyUtil;
import com.small.nacos.api.config.ConfigService;
import com.small.nacos.api.config.listener.AbstractSharedListener;
import com.small.nacos.api.config.listener.Listener;
import com.small.nacos.api.exception.NacosException;
import com.springboot.nacos.config.NacosConfigManager;
import com.springboot.nacos.config.NacosConfigProperties;
import com.springboot.nacos.config.NacosPropertySourceRepository;
import com.springboot.nacos.config.client.NacosPropertySource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.endpoint.event.RefreshEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

public class NacosContextRefresher implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware {

    private final static Logger LOGGER = LoggerFactory
            .getLogger(NacosContextRefresher.class);

    private ApplicationContext applicationContext;

    private final ConfigService configService;

    private NacosConfigProperties nacosConfigProperties;

    /**
     * 刷新次数
     */
    private static final AtomicLong REFRESH_COUNT = new AtomicLong(0);

    /**
     * 监听器缓存
     */
    private Map<String, Listener> listenerMap = new ConcurrentHashMap<>(16);

    public NacosContextRefresher(NacosConfigManager nacosConfigManager) {
        this.nacosConfigProperties = nacosConfigManager.getNacosConfigProperties();
        this.configService = nacosConfigManager.getConfigService();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        if (nacosConfigProperties.isRefreshEnabled()) {
            for (NacosPropertySource propertySource : NacosPropertySourceRepository.getAll()) {
                if (!propertySource.isRefreshable()) {
                    continue;
                }
                String dataId = propertySource.getDataId();
                registerNacosListener(propertySource.getGroup(), dataId);
            }
        }
    }

    private void registerNacosListener(final String groupKey, final String dataKey) {

        String key = KeyUtil.getKey(dataKey, groupKey);

        Listener listener = listenerMap.computeIfAbsent(key, lst -> new AbstractSharedListener() {
            @Override
            public void innerReceive(String dataId, String group, String configInfo) {
                refreshCountIncrement();
                applicationContext.publishEvent(
                        new RefreshEvent(this, null, "Refresh Nacos config"));
            }
        });

        try {
            configService.addListener(dataKey, groupKey, listener);
            LOGGER.info("[Nacos Config] Listening config: dataId={}, group={}", dataKey,
                    groupKey);
        } catch (NacosException e) {
            LOGGER.warn(String.format(
                    "register fail for nacos listener ,dataId=[%s],group=[%s]", dataKey,
                    groupKey), e);
        }
    }

    public static long getRefreshCount() {
        return REFRESH_COUNT.get();
    }

    public static void refreshCountIncrement() {
        REFRESH_COUNT.incrementAndGet();
    }
}
